//----------------------------------------------------------------------------------------------------------------------
// Copyright (c) 2012 James Whitworth
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the \"Software\"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//----------------------------------------------------------------------------------------------------------------------

// this file parses command line arguments setting various global options

// all valid command line options
//
local codegen_options = {
  help = {
    shortname = "h",
    longname = "help",
    optiontype = "flag",
    value = false,
    helptext = "prints this help text.",
  },
  verbose = {
    shortname = "v",
    longname = "verbose",
    optiontype = "flag",
    value = false,
    helptext = "prints out extra logging information.",
  },
  type = {
    shortname = "t",
    longname = "type",
    optiontype = "string",
    value = "header",
    validate = function(value)
    {
      switch (value)
      {
      case "header":
      case "inline":
      case "source":
        return true
      default:
        return false
      }
    },
    helptext = "Which c++ file type to output, valid types are 'header', 'inline' and 'source'.",
  },
  file = {
    shortname = "f",
    longname = "file",
    optiontype = "string",
    value = "out.h",
    validate = function(value)
    {
      if (type(value) != "string")
      {
        return false
      }
      return true
    },
    helptext = "The name of the output file with no extension.",
  },
  iterations = {
    shortname = "i",
    longname = "iterations",
    optiontype = "integer",
    minimum = 0,
    value = 10,
    helptext = "The maximum argument count for each generated function, ie -i 5 means between 0-5 arguments."
  },
  arguments = {
    shortname = "args",
    longname = "arguments",
    optiontype = "integer",
    minimum = 0,
    value = 10,
    helptext = "The argument count for this generated file, ie -i 5 means between 5 argument functions."
  },
  author = {
    shortname = "a",
    longname = "author",
    optiontype = "string",
    value = "James Whitworth",
    helptext = "The name of the author to include in the copywrite notice.",
  },
}

display_usage_text <- function()
{
  print(format("usage: sq %scodegen.nut [options] <codegen-script>\n\n", _SCRIPTS_PATH))
  print("available options are:\n")
  print(format("  %-11s  %-11s  %s\n", "short name", "long name", "description"))
  print(format("  %-11s  %-11s  %s\n", "----------", "---------", "-----------"))
  foreach (name, option in codegen_options)
  {
    print(format("  -%-10s  -%-10s  %s\n", option.shortname, option.longname, option.helptext))
  }
}

display_verbose_text <- function()
{
  print("running codegen...\n")
  foreach (name, option in _OPTIONS)
  {
    switch (name)
    {
    case "help":
    case "verbose":
      break
    default:
      print(format("%s = %s\n", name, _OPTIONS[name].tostring()))
      break
    }
  }
  print(format("<codegen-script> = %s\n\n", _CODEGEN_SCRIPT))
}

// parses arguments given on the command line
//
parse_command_line <- function(args)
{
  _CODEGEN_SCRIPT = null
  _OPTIONS = { }

  local count = args.len()
  if (count == 0)
  {
    return false
  }

  local lookup = {}
  foreach (name, option in codegen_options)
  {
    lookup[option.shortname] <- name
    _OPTIONS[option.longname] <- option.value
  }

  local expression = regexp(@"-(\w*)")
  for (local i = 0; i < count; ++i)
  {
    local results = expression.capture(args[i])

    if (!results || results[1].begin != 1)
    {
      if (i == count - 1)
      {
        _CODEGEN_SCRIPT = args[i]
      }
      else
      {
        print(format("encountered unknown argument '%s', <codegen-script> must be the last argument.\n\n", args[i]))
        return false
      }
    }
    else
    {
      local argument_name = args[i].slice(results[1].begin, results[1].end)

      local option_name = null
      if (argument_name in lookup)
      {
        option_name = lookup[argument_name]
      }
      else if (argument_name in codegen_options)
      {
        option_name = argument_name
      }

      if (option_name)
      {
        local option = codegen_options[option_name]

        switch (option.optiontype)
        {
        case "flag":
          _OPTIONS[option.longname] = true
          break
        case "string":
          ++i
          if (i < count)
          {
            if ("validate" in option)
            {
              if (!option.validate(args[i]))
              {
                print(format("invalid value '%s' for option '%s'.\n\n", args[i], args[i - 1]))
                return false
              }
            }
            _OPTIONS[option.longname] = args[i]
            break
          }

          print(format("option '%s' is missing an argument.\n\n", args[i - 1]))
          return false
        case "integer":
          ++i
          if (i < count)
          {
            local value = null
            try
            {
              value = args[i].tointeger()
            }
            catch (_)
            {
              print(format("invalid value '%s' for option '%s', value must be of type 'integer'.\n\n", args[i], args[i - 1]))
              return false
            }

            if ("minimum" in option)
            {
              if (value < option.minimum)
              {
                return false
              }
            }
            if ("maximum" in option)
            {
              if (value > option.maximum)
              {
                return false
              }
            }
            _OPTIONS[option.longname] = value
            break
          }

          print(format("option '%s' is missing an argument.\n\n", args[i - 1]))
          return false
        default:
          print(format("encountered unknown option type '%s' for option '%s'.\n\n", option.optiontype, args[i]))
          return false
        }
      }
      else
      {
        print(format("unknown option '%s'.\n\n", args[i]))
        return false
      }
    }
  }

  if (_CODEGEN_SCRIPT == null)
  {
    print("missing argument <codegen-script>.\n\n")
    return false
  }

  return true
}